const { validate } = require("validate.js")
const blogDao = require("../dao/operation/blogDao")
const { addBlogToType, oneType, reduceBlogToType } = require("../dao/operation/blogTypeDao")
const blogTypeModel = require("../dao/model/blogTypeModel");
const { ValidationError, NotFoundError } = require("../utils/error")
const { formatResponse, processingData } = require("../utils/tool");
const toc = require("../utils/toc");
const { deleteMessageByBlogIdDao } = require("../dao/operation/messageDao")

// 扩展验证规则
validate.validators.categoryIdIsExist = async function(value) {
    const blogTypeInfo = blogTypeModel.findByPk(value);
    if (blogTypeInfo) {
        return;
    }
    return "CategoryId Is Not Exist";
}

/**
 * 新增
 * @param {*} newType 
 */
module.exports.addBlog = async function(info) {
    // 数据验证规则

    // Toc处理
    info = toc.handleTOC(info)
        // 初始化其他信息

    info.scanNumber = 0 // 阅读量
    info.commentNumber = 0 // 评论数

    // 定义验证规则
    const rule = {
            title: {
                presence: {
                    allowEmpty: false
                },
                type: "string"
            },
            description: {
                presence: {
                    allowEmpty: true
                },
                type: "string"
            },
            toc: {
                presence: {
                    allowEmpty: true
                },
                type: "string"
            },
            htmlContent: {
                presence: {
                    allowEmpty: false
                },
                type: "string"
            },
            thumb: {
                presence: {
                    allowEmpty: true
                },
                type: "string"
            },
            scanNumber: {
                presence: {
                    allowEmpty: false
                },
                type: "integer"
            },
            commentNumber: {
                presence: {
                    allowEmpty: false
                },
                type: "integer"
            },
            createDate: {
                presence: {
                    allowEmpty: false
                },
                type: "integer"
            },
            categoryId: {
                presence: true,
                type: "integer",
                categoryIdIsExist: true
            }
        }
        // 接下来对传递过来的数据进行一个验证
    try {
        // 因为扩展的验证规则里面涉及到异步的操作，所以这里要采用异步的验证方式
        await validate.async(info, rule);
        console.log(info);
        const data = await blogDao.addBlog(info); // 进行一个新增
        // 接下来还有一个工作，文章新增了，对应的文章分类也应该新增
        await addBlogToType(info.categoryId);
        return formatResponse(0, "", data);
    } catch (e) {
        // console.log(e, ">>>");
        // 验证未通过
        return new ValidationError("数据验证失败").toResponseJSONError()
    }
}

/**
 * 分页查询所有
 * @param {*} pageInfo 
 */
module.exports.findblogByPage = async function(pageInfo) {
    const data = await blogDao.findBlogByPageDao(pageInfo);
    const rows = processingData(data.rows);
    // 针对 TOC 要做一个还原的操作
    rows.forEach(it => {
        it.toc = JSON.parse(it.toc);
    });
    if (data) {
        return formatResponse(0, "", {
            "total": data.count,
            "rows": rows
        });
    }
    return new NotFoundError().toResponseJSONError()
}





/**
 * 获取单篇文章
 * @param {*} id  // 单篇文章的id
 * @param {*} auth 
 * @returns 
 */
module.exports.findBlogById = async function(id, auth) {
    const data = await blogDao.findBlogByIdDao(id);
    // 首先需要重新处理 TOC，还原成一个数组
    data.dataValues.toc = JSON.parse(data.dataValues.toc);
    // 根据 auth 是否有值来决定浏览数是否要自增
    if (!auth) {
        data.scanNumber++;
        await data.save();
    }
    if (data.dataValues) {
        return formatResponse(0, "", data.dataValues);
    }
    return new NotFoundError().toResponseJSONError()
}


/**
 * 根据id删除
 * @param {*} id 
 * @returns 
 */
module.exports.deleteBlog = async function(id) {
    // 根据 id 查询到该篇文章的信息
    const data = await blogDao.findBlogByIdDao(id);
    // 接下来需要根据该文章对应的分类，该分类下的文章数量自减

    await reduceBlogToType(data.dataValues.categoryId);
    // 之后还有一个操作，就是该文章下所对应的评论也要一并删除

    await deleteMessageByBlogIdDao(id);


    // 之后就可以删除这篇文章了
    await blogDao.deleteBlogDao(id);
    return formatResponse(0, "", true);
}




// 修改一篇博文
module.exports.updateBlog = async function(id, newBlogInfo) {


    // 首先判断正文内容有没有改变，因为正文内容的改变会影响 TOC
    if (newBlogInfo.htmlContent) {
        // 进入此 if，说明文章的正文内容有所改变，需要重新处理 TOC 目录
        newBlogInfo = toc.handleTOC(newBlogInfo);
    }

    // 这里涉及到一个问题，就是文章分类有没有修改，如果有修改，之前的文章分类对应的文章数量要自减
    // 新的文章分类对应的文章数量要自增
    const { dataValues: oldBlogInfo } = await blogDao.findBlogByIdDao(id);

    if (newBlogInfo.categoryId !== oldBlogInfo.categoryId) {
        // 如果进入此 if，说明修改了此文章的分类信息，那么修改前后的文章分类对应的文章数量都需要做出修改

        // 旧的自减
        await reduceBlogToType(oldBlogInfo.categoryId);

        // 新的自增
        await addBlogToType(newBlogInfo.categoryId);
    }

    const data = await blogDao.updateBlogDao(id, newBlogInfo);
    if (data) {
        return formatResponse(0, "", data.dataValues);
    }
    return new NotFoundError().toResponseJSONError()
}